import { PopoverConfirm } from '@/components';
import { TABLE_KEYS } from '@/constants/componentKeys';
import { forkHandler, pick } from '@/utils';
import { Button, Space } from 'antd';
import * as PropTypes from 'prop-types';
import React, { Children, cloneElement, isValidElement, memo } from 'react';
import { useCall, useCreateCall, useSimpleMemo, useToggle } from '.';
import createTransportHooks from './createTransportHooks';

const [provideActions, useActions] = createTransportHooks();

const getHandler = (key, props) => {
  const pascal = key.replace(/(^[a-z])|([_-][a-z])/g, (i) => i.substr(-1).toUpperCase());
  return props?.[`on${pascal}`];
};

const fn = (text, ...args) => (typeof text === 'function' ? text(...args) : text);

function WrapButton(props) {
  let { children, ...rest } = props;
  const [loading, toggle] = useToggle();
  const handleClick = useCall(forkHandler(toggle, children?.props?.onClick, rest.onClick, toggle, true));

  if (children == null || children === false) return null;

  if (typeof children === 'string') {
    return (
      <Button size='small' loading={loading} onClick={handleClick} {...rest}>
        {children}
      </Button>
    );
  }
  if (isValidElement(children) && Children.count(children) === 1 && children.props.hasOwnProperty('loading')) {
    return cloneElement(children, { ...rest, onClick: handleClick, loading });
  }
  return cloneElement(children, rest);
}

WrapButton = memo(WrapButton);

WrapButton.propTypes = { children: PropTypes.any };

function ActionDialog(props) {
  let { action, row, index, handler, after, props: btnProps } = props;
  const { dialog: Comp, text, onOk, onCancel, itemProp = 'row', ...rest } = action;
  return (
    <Comp
      {...rest}
      {...{ [itemProp]: row }}
      index={index}
      onOk={useCall(forkHandler(handler, onOk, after, true), row, index)}
      onCancel={useCall(onCancel, row, index)}
    >
      <WrapButton {...btnProps}>{fn(text, row, index)}</WrapButton>
    </Comp>
  );
}

ActionDialog = memo(ActionDialog);

ActionDialog.propTypes = {
  action: PropTypes.shape({
    /** 用ClickModal封装的组件 */
    dialog: PropTypes.elementType.isRequired,
    /** 按钮文字 */
    text: PropTypes.oneOfType([PropTypes.func, PropTypes.node]).isRequired,
    /** 组件点击确认回调，可获得 (row, index, ...组件回调参数)  */
    onOk: PropTypes.func,
    /**  */
    onCancel: PropTypes.func,
  }).isRequired,
  row: PropTypes.any.isRequired,
  index: PropTypes.number.isRequired,
  props: PropTypes.object,
};

function ActionConfirm(props) {
  let { action, row, index, handler, after, props: btnProps } = props;
  const { confirm = '是否确认删除', text, beforeConfirm, onOk, onCancel, ...rest } = action;
  return (
    <PopoverConfirm
      {...rest}
      content={fn(confirm, row, index)}
      beforeConfirm={useCall(beforeConfirm, row, index)}
      onOk={useCall(forkHandler(handler, onOk, after, true), row, index)}
      onCancel={useCall(onCancel, row, index)}
    >
      <WrapButton {...btnProps}>{fn(text, row, index)}</WrapButton>
    </PopoverConfirm>
  );
}

ActionConfirm = memo(ActionConfirm);

ActionConfirm.propTypes = {
  action: PropTypes.shape({
    text: PropTypes.oneOfType([PropTypes.func, PropTypes.node]).isRequired,
    confirm: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
    onOk: PropTypes.func,
    onCancel: PropTypes.func,
  }),
  row: PropTypes.any.isRequired,
  index: PropTypes.number.isRequired,
  props: PropTypes.object,
};

const actionColumn = {
  key: 'operation',
  fixed: 'right',
  title: '操作',
  align: 'center',
};

function actionColRender(actionCol, _, row, index) {
  const [actions, otherProps] = useActions();
  const onProps = getHandler('actionProps', otherProps);
  const createCall = useCreateCall();

  if (typeof actionCol?.render === 'function') return actionCol.render(_, row, index);

  return (
    <Space size='small'>
      {Object.keys(actions ?? {}).map((key) => {
        const action = actions[key];
        const handler = getHandler(key, otherProps);
        const after = getHandler(`after-${key}`, otherProps);
        const props = onProps?.(key, action);
        const handleClick = createCall.of(key, forkHandler(props?.onClick, handler, true), row, index);
        const onClickNormal = createCall.of(`onClickNormal:${key}`, forkHandler(handleClick, after, true), row, index);

        switch (typeof action) {
          case 'string':
            return (
              <Button size='small' {...props} key={key} onClick={handleClick}>
                {action}
              </Button>
            );

          case 'function':
            const el = action(row, index);
            return (
              <WrapButton key={key} onClick={onClickNormal}>
                {el}
              </WrapButton>
            );

          case 'object':
            const objectKeys = Object.keys(action ?? {});

            if (objectKeys.includes('dialog')) {
              return (
                <ActionDialog
                  key={key}
                  action={action}
                  handler={handler}
                  after={after}
                  props={props}
                  row={row}
                  index={index}
                />
              );
            }

            if (objectKeys.includes('confirm')) {
              return (
                <ActionConfirm
                  key={key}
                  action={action}
                  handler={handler}
                  after={after}
                  props={props}
                  row={row}
                  index={index}
                />
              );
            }

            if (isValidElement(action) && Children.count(action) === 1) {
              const elementClick = createCall.of(
                `cloneElementOf::${key}`,
                forkHandler(action.props.onClick, handleClick, true),
              );
              return cloneElement(action, {
                ...props,
                key,
                onClick: elementClick,
              });
            }

            return null;
        }
      })}
    </Space>
  );
}

function useTableActions(actions, props, actionCol, actionTitle) {
  provideActions([actions, pick(props ?? {}, TABLE_KEYS)[1]]);
  const createCall = useCreateCall();

  let ret = [];
  if (Object.keys(actions || {}).length) {
    ret = [
      {
        ...actionColumn,
        ...actionCol,
        title: actionTitle ?? actionColumn.title,
        render: createCall.of('render', actionColRender, actionCol),
      },
    ];
  }

  return useSimpleMemo(ret);
}

export default useTableActions;
